package com.qipay.utils;


import org.apache.commons.codec.digest.DigestUtils;

import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;

public class MD5Util {

    // 向量
    private final static String iv = "87654321";
    // 加解密统一使用的编码方式
    private final static String encoding = "UTF-8";

    public static final String KEY_ALGORITHM = "RSA";
    public static final String SIGNATURE_ALGORITHMMD = "MD5withRSA";

    /**
     * 3DES加密
     *
     * @param plainText 普通文本
     * @param secretKey
     * @return
     * @throws Exception
     */
    public static String encode(String plainText, String secretKey) {
        try {
            Key deskey = null;
            DESedeKeySpec spec = new DESedeKeySpec(secretKey.getBytes());
            SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
            deskey = keyfactory.generateSecret(spec);

            Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding");
            IvParameterSpec ips = new IvParameterSpec(iv.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, deskey, ips);
            byte[] encryptData = cipher.doFinal(plainText.getBytes(encoding));
            //return Des3Base64.encode(encryptData);
            String bb = Base64.encode(encryptData);
            return bb;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return plainText;
    }

    /**
     * 3DES解密
     *
     * @param encryptText 加密文本
     * @return
     * @throws Exception
     */
    public static String decode(String encryptText, String secretKey) {
        try {
            Key deskey = null;
            DESedeKeySpec spec = new DESedeKeySpec(secretKey.getBytes());
            SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
            deskey = keyfactory.generateSecret(spec);
            Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding");
            IvParameterSpec ips = new IvParameterSpec(iv.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, deskey, ips);

            //byte[] decryptData = cipher.doFinal(Des3Base64.decode(encryptText));
            byte[] decryptData = cipher.doFinal(Base64.decode(encryptText));
            return new String(decryptData, encoding);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return encryptText;
    }

    private static String byteArrayToHexString(byte b[]) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++)
            resultSb.append(byteToHexString(b[i]));

        return resultSb.toString();
    }

    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0)
            n += 256;
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }

    public static String MD5Encode(String origin, String charsetname) {
        String resultString = null;
        try {
            resultString = new String(origin);
            MessageDigest md = MessageDigest.getInstance("MD5");
            if (charsetname == null || "".equals(charsetname))
                resultString = byteArrayToHexString(md.digest(resultString
                        .getBytes()));
            else
                resultString = byteArrayToHexString(md.digest(resultString
                        .getBytes(charsetname)));
        } catch (Exception exception) {
        }
        return resultString;
    }

    public static byte[] MD5Encode0(String origin, String charsetname) {

        String resultString = null;
        byte[] md5bytes = null;
        try {
            resultString = new String(origin);
            MessageDigest md = MessageDigest.getInstance("MD5");
            if (charsetname == null || "".equals(charsetname))
                md5bytes = md.digest(resultString.getBytes());
            else
                md5bytes = md.digest(resultString.getBytes(charsetname));
        } catch (Exception exception) {
        }
        return md5bytes;
    }


    private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};

    public static String getSign(Map<String, Object> maps, String key) {
        SortedMap<String, Object> params = new TreeMap<String, Object>();
        for (String mapKey : maps.keySet()) {
            params.put(mapKey, maps.get(mapKey));
        }

        StringBuilder sb = new StringBuilder();
        for (String keyItem : params.keySet()) {
            if (params.get(keyItem) != null && !params.get(keyItem).toString().equals("")
                    && !keyItem.equals("sign") && !keyItem.equals("Sign") &&
                    !keyItem.equals("signature") && !keyItem.equals("signData")) {
                sb.append(keyItem + "=" + params.get(keyItem).toString() + "&");
            }
        }
        sb.append("key=" + key);
        String bunch = sb.toString();
        return MD5Util.MD5Encode(bunch, "UTF-8");
    }

    public static String getSignNoMapKey(Map<String, Object> maps, String key) {
        SortedMap<String, Object> params = new TreeMap<String, Object>();
        for (String mapKey : maps.keySet()) {
            params.put(mapKey, maps.get(mapKey));
        }

        StringBuilder sb = new StringBuilder();
        for (String keyItem : params.keySet()) {
            if (params.get(keyItem) != null && !params.get(keyItem).toString().equals("")
                    && !keyItem.equals("sign") && !keyItem.equals("Sign") &&
                    !keyItem.equals("signature") && !keyItem.equals("signData")) {
                sb.append(params.get(keyItem).toString());
            }
        }
        sb.append(key);
        return MD5Util.MD5Encode(sb.toString(), "UTF-8");
    }

    public static String getSignNoKey(Map<String, Object> maps, String key) {
        SortedMap<String, Object> params = new TreeMap<String, Object>();
        for (String mapKey : maps.keySet()) {
            params.put(mapKey, maps.get(mapKey));
        }

        StringBuilder sb = new StringBuilder();
        for (String keyItem : params.keySet()) {
            if (params.get(keyItem) != null && !params.get(keyItem).toString().equals("")
                    && !keyItem.equals("sign") && !keyItem.equals("Sign") &&
                    !keyItem.equals("signature") && !keyItem.equals("signData")) {
                sb.append(keyItem + "=" + params.get(keyItem).toString() + "&");
            }
        }
        sb.append(key);
        return MD5Util.MD5Encode(sb.toString(), "UTF-8");
    }

    public static String getRSAValue(Map<String, Object> maps, String key) throws Exception {
        SortedMap<String, Object> params = new TreeMap<String, Object>();
        for (String mapKey : maps.keySet()) {
            params.put(mapKey, maps.get(mapKey));
        }

        StringBuilder sb = new StringBuilder();
        for (String keyItem : params.keySet()) {
            if (params.get(keyItem) != null && !params.get(keyItem).toString().equals("")
                    && !keyItem.equals("sign") && !keyItem.equals("Sign") &&
                    !keyItem.equals("signature") && !keyItem.equals("signData")) {
                sb.append(params.get(keyItem).toString());
            }
        }
        String bunch = sb.toString();
        String sign = RSAUtils.encryptSignByPrivateKey(bunch.getBytes("UTF-8"), key);
        return sign;
    }

    public static String getRSAMD5Sign(Map<String, Object> maps, String key) throws Exception {
        SortedMap<String, Object> params = new TreeMap<>();
        for (String mapKey : maps.keySet()) {
            params.put(mapKey, maps.get(mapKey));
        }
        StringBuilder sb = new StringBuilder();
        for (String keyItem : params.keySet()) {
            sb.append(keyItem).append("=").append(params.get(keyItem).toString()).append("&");
        }
        String bunch = sb.substring(0, sb.length() - 1);
        byte[] keyBytes = Base64.decode(key);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHMMD);
        signature.initSign(privateK);
        signature.update(bunch.getBytes());
        return Base64.encode(signature.sign());
    }


    public static String getSignObject(Map<String, String> maps, String key) {
        SortedMap<String, Object> params = new TreeMap<String, Object>();
        for (String mapKey : maps.keySet()) {
            params.put(mapKey, maps.get(mapKey));
        }

        StringBuilder sb = new StringBuilder();
        for (String keyItem : params.keySet()) {
            if (params.get(keyItem) != null && !params.get(keyItem).toString().equals("")
                    && !keyItem.equals("sign") && !keyItem.equals("Sign") &&
                    !keyItem.equals("signature") && !keyItem.equals("signData")) {
                sb.append(keyItem + "=" + params.get(keyItem).toString() + "&");
            }
        }
        sb.append("key=" + key);
        String bunch = sb.toString();
        return MD5Util.MD5Encode(bunch, "UTF-8");
    }


    /**
     * 签名字符串
     *
     * @param text          需要签名的字符串
     * @param input_charset 编码格式
     * @return 签名结果
     */
    public static String sign(String text, String input_charset) {
        System.out.println(text);
        return DigestUtils.md5Hex(getContentBytes(text, input_charset)).toUpperCase();
    }

    public static String signNoUp(String text, String input_charset) {
        System.out.println(text);
        return DigestUtils.md5Hex(getContentBytes(text, input_charset));
    }

    /**
     * 签名字符串
     *
     * @param text          需要签名的字符串
     * @param sign          签名结果
     * @param input_charset 编码格式
     * @return 签名结果
     */
    public static boolean verify(String text, String sign, String input_charset) {
        String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));
        if (mysign.equals(sign)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * @param content
     * @param charset
     * @return
     * @throws SignatureException
     * @throws UnsupportedEncodingException
     */
    private static byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
        }
    }


}
